package cn.org.timespace.plat.socket.mina.filter;

import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.AttributeKey;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;

import cn.org.timespace.plat.socket.mina.util.ProcessPlatPackage;
import cn.org.timespace.plat.socket.util.MinaPlatIobufferUtil;


public class ESPlatProtocolDecoder implements ProtocolDecoder {

	private final static Log log = LogFactory.getLog(ESPlatProtocolDecoder.class);

	private final AttributeKey CONTEXT = new AttributeKey(getClass(), "context");
	private final Charset charset;
	private int maxPackLength = 1024;

	public ESPlatProtocolDecoder() {
		this(Charset.defaultCharset());
	}

	public ESPlatProtocolDecoder(Charset charset) {
		this.charset = charset;
	}

	public int getMaxLineLength() {
		return maxPackLength;
	}

	public void setMaxLineLength(int maxLineLength) {
		if (maxLineLength <= 0) {
			throw new IllegalArgumentException("maxLineLength: " + maxLineLength);
		}
		this.maxPackLength = maxLineLength;
	}

	private Context getContext(IoSession session) {
		Context ctx;
		ctx = (Context) session.getAttribute(CONTEXT);
		
		if (ctx == null) {
			ctx = new Context();
			session.setAttribute(CONTEXT, ctx);
		}
		
		return ctx;
	}

	public void decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
		// 一个完整的包最少长度,即:同步帧+机器号+包长度+功能号+较验和
		final int minLength = 17;
		
		// 先获取上次的处理上下文，其中可能有未处理完的数据
		Context ctx = getContext(session);
		
		// 如果下一包是以字符串”ZJX“开头
		String ioBufferStr = MinaPlatIobufferUtil.ioBufferToString(in.asReadOnlyBuffer());
		if(ProcessPlatPackage.isStartWithSyncFrame(ioBufferStr)) {
			//丢弃未处理的数据，重新组装包
			ctx.getBuffer().clear();
		}
		
		// 先把当前buffer中的数据追加到Context的buffer当中
		ctx.append(in);
		
		IoBuffer buf = ctx.getBuffer();
		
		// 把position指向0位置，把limit指向原来的position位置
		buf.flip();
		
		// 然后按数据包的协议进行读取
		while (buf.remaining() >= minLength) {
			buf.mark();
			
			int oldLimit = buf.limit();
			
			// 读取消息头部分
			buf.limit(buf.position() + (minLength - 2));
			String head = buf.getString(ctx.getDecoder());
			ESPlatProtocalPack packReceive = new ESPlatProtocalPack();
			packReceive.setSyncFrame(head.substring(0, 3));		//取前3位,同步帧，长度：3
			packReceive.setMachineNo(head.substring(3, 9));		//机器号，长度：6
			packReceive.setPackageLength(head.substring(9, 13));//包长度，长度：4，即2+n("功能号+包内容")的长度
			packReceive.setFunctionNo(head.substring(13, 15));	//功能号，长度：2
			int packageLength = packReceive.getPackageLengthInt();
			buf.limit(oldLimit);
			// 检查读取的包头是否正常，不正常的话清空buffer
			if (packageLength < 0 || packageLength > maxPackLength) {
				log.debug("包不正常,清空buffer");
				buf.clear();
				break;
			}
			// 读取正常的消息包，并写入输出流中，以便IoHandler进行处理
			else if (buf.remaining() >= packageLength) { //如果大于:packageLength-2(功能号)+2(较验和)
				buf.limit(buf.position() + (packageLength - 2));
				packReceive.setPackageContents(buf.getString(ctx.getDecoder()));//包内容，长度n,(即packageLength - 2)
				
				buf.limit(buf.position() + 2);
				packReceive.setCheckCode(buf.getString(ctx.getDecoder()));//较验和，长度：2
				
				buf.limit(oldLimit);
				
				out.write(packReceive);
			} else {
				// 如果消息包不完整，将指针重新移到消息头的起始位置
				log.debug("消息包不完整,等待下一帧");
				buf.reset();
				break;
			}
		}
		
		if (buf.hasRemaining()) {
			IoBuffer temp = IoBuffer.allocate(maxPackLength);
			temp.setAutoExpand(true);//自动扩张
			temp.setAutoShrink(true);//自动收缩

			temp.put(buf);
			temp.flip();
			buf.clear();
			//如果是以ZJX开头
			String ioBufferTemp = MinaPlatIobufferUtil.ioBufferToString(temp.asReadOnlyBuffer());
			if(ProcessPlatPackage.isStartWithSyncFrame(ioBufferTemp)) {
				buf.put(temp);// 将数据移到buffer的最前面
			}
		} else {// 如果数据已经处理完毕，进行清空
			buf.clear();
		}
	}
	
	public void finishDecode(IoSession session, ProtocolDecoderOutput out) throws Exception {
	}

	public void dispose(IoSession session) throws Exception {
		Context ctx = (Context) session.getAttribute(CONTEXT);
		if (ctx != null) {
			session.removeAttribute(CONTEXT);
		}
	}

	// 记录上下文，因为数据触发没有规模，很可能只收到数据包的一半
	// 所以，需要上下文拼起来才能完整的处理
	private class Context {

		private final CharsetDecoder decoder;
		private IoBuffer buf;
		private int matchCount = 0;
		private int overflowPosition = 0;

		private Context() {
			decoder = charset.newDecoder();
			buf = IoBuffer.allocate(80);
			buf.setAutoExpand(true);
			buf.setAutoShrink(true);
		}

		public CharsetDecoder getDecoder() {
			return decoder;
		}

		public IoBuffer getBuffer() {
			return buf;
		}

		public int getOverflowPosition() {
			return overflowPosition;
		}

		public int getMatchCount() {
			return matchCount;
		}

		public void setMatchCount(int matchCount) {
			this.matchCount = matchCount;
		}

		public void reset() {
			overflowPosition = 0;
			matchCount = 0;
			decoder.reset();
		}

		public void append(IoBuffer in) {
			getBuffer().put(in);
		}
	}
}
